#!/usr/bin/env perl
use strict;
use warnings;
use File::Lockfile;
use Switch;
use Parallel::ForkManager;
use LWP::UserAgent;
use LWP::Simple;
use HTML::TreeBuilder::XPath;
use Encode;
use JSON;
use YAML qw'LoadFile Dump';
use utf8;

binmode(STDIN,':encoding(utf8)');
binmode(STDOUT,':encoding(utf8)');
binmode(STDERR,':encoding(utf8)');

my %comiclist = ();
my $indexdatafilename = ".CBDComic.yaml";
my $comicdl_version = "2011.07.06";
my $max_fork = 5;

#create lockfile
my $lockfile = 0;
sub lockdir {
    $lockfile = File::Lockfile->new('.CBDLock','.');
    if ( my $pid = $lockfile->check ) {
        print "当前目录已被PID:".$pid."锁定!\n";
        exit;
    };
    $lockfile->write;
};


sub get_web {
    my $url = shift;
    my $agent = new LWP::UserAgent(
        agent => 'Mozilla/5.0',
        cookie_jar => {}
    );
    my $res =  $agent->get($url);
    my $count = 1;
    while ( !$res->is_success() && $count <= 11 ) {
        $res = $agent->get($url);
        $count++;
    };
    print "无法获取$url!\n" if !$res->is_success(); 
    return $res->decoded_content();
};

#search comic @SFACG
sub search_sfacg {
    my $keyword = shift;
    my $wc = get_web('http://s.sfacg.com/?Key='.$keyword.'&S=0&SS=0');
    my $tree = new HTML::TreeBuilder::XPath;
    $tree->parse($wc);
    $tree->eof;
    my $items = $tree->findnodes( '//ul[@style="width:100%"]' );
    foreach my $item ( $items->get_nodelist() ) {
        if ( $item->toString() =~ m/alt="(.*?)".*href="(.*?)".*<br \/>(.*?)<\/li><\/ul>/ ) {
            my %comicstruct = (
                name => $1,
                description => $3,
                url => $2 
            );
            $comiclist{$1} = \%comicstruct; 
        };
    };
};

#search comic @178
sub search_178 {
    my $keyword = shift;
    my $wc = get_web('http://s.acg.178.com/comicsum/search.php?s='.$keyword);
    if ( $wc =~ m/(\[.*\])/ ) {
        my $jsondata = decode_json $1;
        foreach my $comicitem (@{$jsondata}) {
            my %comicstruct = (
                name => $comicitem->{"name"},
                description => $comicitem->{"description"},
                url => "http://manhua.178.com/".$comicitem->{"comic_py"}
            );
            $comiclist{$comicitem->{"name"}} = \%comicstruct; 
        };
    };
};

#print searchdata 
sub print_searchdata {
    my $is_info = shift;
    foreach my $key (keys %comiclist) {
        print ${$comiclist{$key}}{name}."\n";
        print ${$comiclist{$key}}{url}."\n";
        print "  ".${$comiclist{$key}}{description}."\n" if $is_info;
        print "\n";
    };
};

#analyse volume @SFACG
sub analyse_volume_sfacg {
    my $url = shift;
    my $wc = get_web($url);
    if ( $url !~ m/http:\/\/\w.*\.sfacg\.com\/.*/) {
        die '非法SFACG链接';
    };
    my %volumelist = ();
    while ( $wc =~ m/<li><a href="(http:\/\/\w*\.sfacg\.com\/AllComic\/.*?\/.*?\/)" target="_blank">(.*?)<\/a><\/li>/ig ) {
        my %volume = (
            name => $2,
            url => $1,
            pic => ''
        );
        $volumelist{$1} = \%volume;
    };
    return \%volumelist;
};

#analyse volume @178
sub analyse_volume_178 {
    my $url = shift;
    my $wc = get_web($url);
    my $uname;
    if ( $url =~ m/http:\/\/manhua\.178\.com\/(\w*)/ ) {
        $uname = $1;
    } else {
        die '非法178链接!';
    };
    my %volumelist = ();
    while ( $wc =~ m/href="(http:\/\/www\.178\.com\/mh\/$uname\/.*?shtml)".*?>(.*?)<\/a><\/li>/ig ) {
        my %volume = (
            name => $2,
            url => $1,
            pic => ''
        );
        $volumelist{$1} = \%volume;
    };
    return \%volumelist;
};

#analyse piclist @SFACG
sub analyse_piclist_sfacg {
    my $url = shift;
    $url =~ s/http:\/\/(\w*\.sfacg\.com\/)AllComic(\/.*)\//http:\/\/$1Utility$2.js/;
    my $wc = get_web($url);
    my @piclist = ();
    while ( $wc =~ m/= "(http:\/\/\w*?\.sfacg\.com\/Pic\/.*?\.(png|jpg|gif))";/ig ) {
        push(@piclist, $1);
    };
    return @piclist;
};

#analyse piclist @178
sub analyse_piclist_178 {
    my $url = shift;
    my $wc = get_web($url);
    my @piclist = ();
    if ( $wc =~ m/var pages = '(\[.*\])';/ ) {
        my $jsondata = decode_json $1;
        foreach my $picitem (@{$jsondata}) {
            my $piclink = "http://manhua.178.com/imgs/".$picitem;
            push(@piclist, $piclink);
        };
    };
    return @piclist;
};

#analyse @SFACG
sub analyse_sfacg {
    my $url = shift;
    my $volumelist = analyse_volume_sfacg($url);
    if ( keys %$volumelist == 0 ) {
        die "获取漫画信息失败!\n";
    };
    foreach my $key (keys %$volumelist) {
        my @pic = analyse_piclist_sfacg($key);
        if ( !@pic ) {
            print "获取 ".$volumelist->{$key}->{name}." 图片链接失败!\n"; 
            delete $volumelist->{$key};
            next;
        };
        $volumelist->{$key}->{pic} = \@pic;
    };
    return $volumelist;
};

#analyse @178
sub analyse_178 {
    my $url = shift;
    my $volumelist = analyse_volume_178($url);
    if ( keys %$volumelist == 0 ) {
        die "获取漫画信息失败!\n";
    };
    foreach my $key (keys %$volumelist) {
        my @pic = analyse_piclist_178($key);
        if ( !@pic ) {
            print "获取 ".$volumelist->{$key}->{name}." 图片链接失败!\n";
            delete $volumelist->{$key};
            next;
        };
        $volumelist->{$key}->{pic} = \@pic;
    };
    return $volumelist;
};

#doanload file
sub download_file {
    my $url = shift;
    my $filename = shift;
    my $count = 1;
    my $status = getstore($url, $filename);
    while ( !is_success($status) && $count <= 10 ) {
        $status = getstore($url, $filename);
        $count++;
    };
    if ( !is_success($status) ) {
        print "无法下载".$url."\n";
    };
};

#download by list
sub download_piclist {
    my $links = shift;
    my $folder = shift;
    my $pm = new Parallel::ForkManager($max_fork);
    chdir($folder);
    foreach my $link (@$links) {
        $pm->start and next;
        if ( $link =~ m/.*\/(.*?.(png|jpg|gif))/i ) {
            my $downloadpath = $1;
            if (-e $downloadpath) {
                print "跳过".$link."\n";
            } else {
                download_file($link, $downloadpath); 
            };
        };
        $pm->finish;
    };
    $pm->wait_all_children;
    chdir("..");
};

#check pic file by list
sub download_check_piclist {
    my $links = shift;
    my $folder = shift;
    my $pm = new Parallel::ForkManager($max_fork);
    chdir($folder);
    foreach my $link (@$links) {
        $pm->start and next;
        if ( $link =~ m/.*\/(.*?.(png|jpg|gif))/i ) {
            my $downloadpath = $1;
            if (!-e $downloadpath) {
                download_file($link, $downloadpath); 
            };
        };
        $pm->finish;
    };
    $pm->wait_all_children;
    chdir("..");
};

#create @SFACG
sub create_sfacg {
    my $url = shift;
    my $folder = shift;
    if ( !-e $folder ) {
        mkdir($folder) or die "无法创建目录"."$folder";
    };
    chdir($folder);
    lockdir;
    print "创建索引中\n";
    my $volumelist = analyse_sfacg($url);

    my $comic = {
        name => $folder,
        url => $url,
        data => $volumelist
    };
    open(my $indexdata, ">:encoding(UTF-8)",  $indexdatafilename) or die "无法写入索引:$!\n";
    print $indexdata Dump($comic);
    close $indexdata;
    foreach my $key (keys %$volumelist) {
        my $volumefolder = $volumelist->{$key}->{name};
        print "开始下载"."$volumefolder\n";
        if ( !-e $volumefolder ) {
            if ( !mkdir($volumefolder) ) {
                die "无法创建目录"."$volumefolder\n";
            };
        };
        download_piclist($volumelist->{$key}->{pic}, $volumefolder);
    };
};

#create @178
sub create_178 {
    my $url = shift;
    my $folder = shift;
    if ( !-e $folder ) {
        mkdir($folder) or die "无法创建目录"."$folder";
    };
    chdir($folder);
    lockdir;
    print "创建索引中\n";
    my $volumelist = analyse_178($url);
    my $comic = {
        name => $folder,
        url => $url,
        data => $volumelist
    };
    open(my $indexdata, ">:encoding(UTF-8)",  $indexdatafilename) or die "无法写入索引:$!\n";
    print $indexdata Dump($comic);
    close $indexdata;
    foreach my $key (keys %$volumelist) {
        my $volumefolder = $volumelist->{$key}->{name};
        print "开始下载"."$volumefolder\n";
        if ( !-e $volumefolder ) {
            if ( !mkdir($volumefolder) ) {
                die "无法创建目录"."$volumefolder\n";
            };
        };
        download_piclist($volumelist->{$key}->{pic}, $volumefolder);
    };
};

#update
sub update {
    my $comic = YAML::LoadFile($indexdatafilename);
    my $url = $comic->{url};
    my $volumelist = $comic->{data};
    my $sites = 0;
    my $newvolumelist;

    if ( $url =~ m/http:\/\/\w.*\.sfacg\.com\/\w.*/ ) {
        $newvolumelist = analyse_volume_sfacg($url);
        $sites = 0;
    } elsif ( $url =~ m/http:\/\/manhua\.178\.com\/\w.*/ ) {
        $newvolumelist = analyse_volume_178($url);
        $sites = 1;
    } else {
        die "网址无效!\n";
    };


    foreach my $key (keys %$newvolumelist) {
        if (defined($volumelist->{$key})) {
            next;
        } else {
            print "更新索引".$newvolumelist->{$key}->{name}."\n";
            my @pic = ();
            if ($sites == 0 ) {
                @pic = analyse_piclist_sfacg($key);
            } elsif ($sites == 1 ) {
                @pic = analyse_piclist_178($key);
            };
            $newvolumelist->{$key}->{pic} = \@pic;
            $volumelist->{$key} = $newvolumelist->{$key}; 
        };
    };
    print "索引更新完毕\n";

    open(my $indexdata, ">:encoding(UTF-8)",  $indexdatafilename) or die "无法写入索引:$!\n";
    print $indexdata Dump($comic);
    close $indexdata;

    foreach my $key (keys %$volumelist) {
        my $volumefolder = $volumelist->{$key}->{name};
        if ( !-e $volumefolder ) {
            print "开始下载"."$volumefolder\n";
            mkdir($volumefolder) or  die "无法创建目录"."$volumefolder\n";
            download_piclist($volumelist->{$key}->{pic}, $volumefolder);
        } else {
            print "检查下载"."$volumefolder\n";
            download_check_piclist($volumelist->{$key}->{pic}, $volumefolder);
        };
    };
};

#print help
sub print_help {
    print "Comic Book Downloader $comicdl_version\n"
         ."使用方法:\n"
         .'comicdl.pl create <url> <name>'."\n"
         ."           创建漫画下载\n\n"
         .'           search <keyword>'."\n"
         ."           搜索漫画 显示名称和链接\n\n"
         .'           search-info <keyword>'."\n"
         ."           搜索漫画 显示名称、链接和简介\n\n"
         .'           update'."\n"
         ."           更新当前目录\n\n"
         .'           update-all'."\n"
         ."           更新当前目录的所有子目录\n";
};

#main loop
if (!defined($ARGV[0])) {
    die print_help;
};

switch ($ARGV[0]) {
    case { ($ARGV[0] eq "search") or ($ARGV[0] eq "search-info") } {
        if ( !defined($ARGV[1]) ) {
            die "请指定关键词!\n";
        };
        my $is_info = 0;
        $is_info = 1 if $ARGV[0] eq "search-info";
        my $search_keyword = $ARGV[1];
        my $length = 0;

        print '正在搜索 @SFACG'."\n";
        search_sfacg($search_keyword);
        $length = keys %comiclist;
        print "$length"."项找到!\n\n";
        print_searchdata($is_info) if $length;
        
        %comiclist = ();

        print '正在搜索 @178'."\n";
        search_178($search_keyword);
        $length = keys %comiclist;
        print "$length"."项找到!\n\n";
        print_searchdata($is_info) if $length;
    }
    case "create" {
        if ( !defined($ARGV[1]) ) {
            die "请指定网址!\n";
        } elsif ( !defined($ARGV[2]) ) {
            die "请指定目录!\n";
        };

        my $url = $ARGV[1];
        my $dirname = $ARGV[2];
        
        if ( -e $dirname ) {
            die "目录已存在!";
        };

        if ( $url =~ m/http:\/\/\w.*\.sfacg\.com\/\w.*/ ) {
            create_sfacg($url, $dirname);
        } elsif ( $url =~ m/http:\/\/manhua\.178\.com\/\w.*/ ) {
            create_178($url, $dirname);
        } else {
            die "请输入有效网址!\n";
        };
    }
    case "update" {
        lockdir;
        if ( !-e $indexdatafilename ) {
            print "无法找到索引文件!\n";
        } else {
            update;
        };
    }
    case "update-all" {
        my $maindir;
        opendir($maindir, '.') or die "无法打开当前目录!\n";
        while (my $subdir = readdir $maindir) {
            if ( -d $subdir && $subdir ne '.' && $subdir ne '..' ) {
                chdir($subdir);
                if ( -e $indexdatafilename ) {
                    print "开始更新目录$subdir\n";
                    lockdir;
                    update;
                    $lockfile->remove;
                } else {
                    print "跳过目录$subdir\n";
                };
                chdir('..');
            } else {
                next;
            };
        };
        close $maindir;
    }
    else {
        print_help;
    }
};

#unlock current dir
if ($lockfile) {
    $lockfile->remove;
};
